package com.itbupt.eat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itbupt.eat.common.CustomException;
import com.itbupt.eat.dto.DishDto;
import com.itbupt.eat.entity.Dish;
import com.itbupt.eat.entity.DishFlavor;
import com.itbupt.eat.entity.Setmeal;
import com.itbupt.eat.mapper.DishMapper;
import com.itbupt.eat.service.IDishService;
import com.itbupt.eat.service.ISetmealService;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author BUPT-Dingchang
 * @Create 2024/4/30 9:46
 * ClassName: DishServiceImpl
 * Description:
 */
@Slf4j
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements IDishService {

    @Autowired
    private DishFlavorServiceImpl dishFlavorService;

    @Autowired
    @Lazy
    private CategoryServiceImpl categoryService;

    @Autowired
    private ISetmealService setmealService;


    /**
     * 新增菜品并设置口味信息
     * @param dishDto 菜品信息
     * @return 是否成功
     */
    @Override
    @Transactional
    public Boolean saveWithFlavor(DishDto dishDto) {

        // 1. 保存菜品基本信息
        this.save(dishDto);

        // 2. 获取菜品id
        Long dishId = dishDto.getId();

        // 3. 便利菜品口味并逐一赋上菜品id值，然后保存
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().peek(flavor -> flavor.setDishId(dishId)).collect(Collectors.toList());

        // 调用saveBatch方法批量保存
        return dishFlavorService.saveBatch(flavors);
    }


    /**
     * 分类查询+按菜品名称查询
     * @param page 当前页
     * @param pageSize  每页显示条数
     * @param name  菜品名称
     * @return 分页数据
     */
    @Override
    public Page<DishDto> getPage(Long page, Long pageSize, String name) {

        // 1. 创建分页构造器
        Page<Dish> dishPage = new Page<>(page, pageSize);
        // 1.1 用于解决浏览器显示菜品分类名称，但需要给分页构造器赋值，采用复制dishPage的方法
        Page<DishDto> dishDtoPage = new Page<>();

        // 2.创建查询条件构造器
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        // 3.添加过滤添加，按菜品名称查询
        lqw.like(Strings.isNotEmpty(name), Dish::getName, name);
        // 4.添加排序条件，按菜品更新时间降序排列
        lqw.orderByDesc(Dish::getUpdateTime);
        // 5.调用数据层的分页查询方法，此时dishPage中已经有值了
        page(dishPage, lqw);

        // 6. 对象拷贝
        // 6.1 把dishPage中的属性值复制到dishDtoPage中，
        // 忽略dishPage中的records
        // 因为records中的数据是真正展示到浏览器上的，我们要处理一下records中的数据
        BeanUtils.copyProperties(dishPage, dishDtoPage, "records");

        // 7.使用stream处理dishRecords集合，目的是处理成dishDtoRecords的集合
        // 7.1 获取dishPage的records
        List<Dish> dishRecords = dishPage.getRecords();
        // 7.2 使用stream处理dishRecords集合，目的是要根据分类ID查询分类表，最终获得分类名称
        List<DishDto> dishDtoRecords = dishRecords.stream().map(dish -> {

             // 7.2.1 获取每个Dish的分类id
            Long categoryId = dish.getCategoryId();

            // 7.2.2 根据分类ID查询分类表，最终获得分类名称
            String categoryName = categoryService.getById(categoryId).getName();

            // 7.2.3 创建DishDto对象
            DishDto dishDto = new DishDto();

            // 7.2.4 先把Dish对象的所有属性拷贝到DishDto对象，然后再设置刚刚获得的分类名称
            BeanUtils.copyProperties(dish, dishDto);

            // 7.2.5 设置分类名称
            dishDto.setCategoryName(categoryName);

            // 7.2.6 返回全部赋值完成的dishDto
            return dishDto;

        }).collect(Collectors.toList());

        // 8. 将处理好的dishRecords集合赋值回dishDtoPage中
        dishDtoPage.setRecords(dishDtoRecords);

        return dishDtoPage;
    }


    /**
     * 根据菜品ID查询菜品信息和对应的口味信息
     * @param id 菜品id
     * @return 返回菜品口味信息
     */
    @Override
    public DishDto getByIdWithFlavor(Long id) {

        // 1. 根据id查询菜品基本信息
        Dish dish = getById(id);

        // 2. 查询菜品口味
        LambdaQueryWrapper<DishFlavor> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DishFlavor::getDishId, id);
        List<DishFlavor> flavors = dishFlavorService.list(wrapper);

        // 3. 创建要返回的DishDto对象
        DishDto dishDto = new DishDto();

        // 4. 将dish复制到dishDto中
        BeanUtils.copyProperties(dish, dishDto);

        // 5. 将菜品口味封装到dishdto中
        dishDto.setFlavors(flavors);

        return dishDto;
    }


    /**
     * 修改菜品--同时修改菜品对应的口味数据
     * @param dishDto 菜品信息
     * @return 返回是否修改成功
     */
    @Override
    @Transactional
    public Boolean updateWithFlavor(DishDto dishDto) {
        // 1. 先删除菜品口味表中对应菜品的口味
        LambdaQueryWrapper<DishFlavor> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DishFlavor::getDishId, dishDto.getId());
        dishFlavorService.remove(wrapper);

        // 2. 获取dishDto中的菜品口味信息
        List<DishFlavor> flavors = dishDto.getFlavors();

        // 3. 使用stream流操作为菜品口味集合设置对应的菜品ID
        flavors = flavors.stream().peek(flavor ->
                flavor.setDishId(dishDto.getId())
        ).collect(Collectors.toList());

        // 4. 批量添加修改后的菜品口味到菜品口味表中
        dishFlavorService.saveBatch(flavors);

        // 5. 更新菜品信息
        return this.updateById(dishDto);
    }


    /**
     * (批量)起售/停售商品
     * @param status    要停售还是要起售
     * @param ids 商品id列表
     * @return 是否操作成功
     */
    @Override
    public Boolean updateStatus(Integer status, List<Long> ids) {

        // 业务逻辑：如果要停售必须检查关联的套餐是否停售
        if(status == 0) {
            // 根据菜品的ids获取其关联的套餐的ids
            Set<Long> setmealIds = setmealService.getIdsByDishId(ids);
            // 如果该菜品关联的套餐ids不为空，才进行下一步
            if(!setmealIds.isEmpty()) {
                // 创建setmeal的过滤条件封装器
                LambdaQueryWrapper<Setmeal> wrapper = new LambdaQueryWrapper<>();
                // 添加过滤条件--IN()套餐IDs
                wrapper.in(Setmeal::getId, setmealIds);
                // 添加过滤条件：在售套餐
                wrapper.eq(Setmeal::getStatus, 1);
                // 如果满足的setmeal记录数大于0，则说明关联套餐在售，抛出业务异常
                if(setmealService.count(wrapper) > 0) {
                    throw new CustomException("停售失败，菜品所关联套餐仍在售，请停售相关套餐");
                }
            }
        }

        // 根据菜品id批量查询菜品
        List<Dish> dishes = this.listByIds(ids);

        // 使用stream流改变售卖状态
        dishes = dishes.stream().peek(dish -> {
            dish.setStatus(status);
        }).collect(Collectors.toList());

        // 批量update
        return this.updateBatchById(dishes);
    }


    /**
     * 删除/批量删除菜品
     * @param ids   菜品id列表
     * @return  是否操作成功
     */
    @Override
    public Boolean removeWithFlavor(List<Long> ids) {

        // 1. 判断待删除的菜品是否正在售卖
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Dish::getId, ids);
        wrapper.eq(Dish::getStatus, 1);
        List<Dish> dishes = this.list(wrapper);
        if(!dishes.isEmpty()) {
            // 获取在售菜品名称
            List<String> dishNames = dishes.stream().map(Dish::getName).collect(Collectors.toList());
            throw new CustomException(dishNames + "正在售卖，删除失败，请停售后重试");
        }

        // 2. 判断待删除的菜品关联套餐是否在售
        Set<Long> setmealIds = setmealService.getIdsByDishId(ids);
        LambdaQueryWrapper<Setmeal> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.in(Setmeal::getId, setmealIds);
        List<Setmeal> setmeals = setmealService.list(wrapper1);
        if(!setmeals.isEmpty()) {
            // 获取绑定套餐名称，方便用户删除对应套餐
            List<String> setmealNames = setmeals.stream().map(Setmeal::getName).collect(Collectors.toList());
            // 把绑定套餐名称也返回到前端页面，方便用户删除对应套餐
            throw new CustomException(setmealNames + "套餐正在售卖该菜品，删除失败，请删除套餐后重试");
        }

        // 批量删除菜品
        this.removeByIds(ids);

        // 删除菜品口味
        LambdaQueryWrapper<DishFlavor> dishFlavorWrapper = new LambdaQueryWrapper<>();
        dishFlavorWrapper.in(DishFlavor::getDishId, ids);

        return dishFlavorService.remove(dishFlavorWrapper);

    }


    /**
     * 根据指定的过滤条件查询菜品信息
     * @param dish  过滤条件
     * @return  菜品信息
     */
    @Override
    public List<DishDto> listWithFlavor(Dish dish) {
        // 1. 创建查询条件构造器
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        // 2. 添加过滤条件--根据分类id查询菜品
        wrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
        // 3. 添加排序条件：根据sort字段升序排列菜品，再根据最后修改时间降序排列
        wrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
        // 4. 过滤条件--只查询在售菜品
        wrapper.eq(Dish::getStatus, 1);
        // 5.调用数据层的查询方法
        List<Dish> dishes = this.list(wrapper);

        // 6.使用集合的stream流逐一把dish封装成dishDto
        return dishes.stream().map(dishItem -> {
            // 6.1 创建DishDto对象
            DishDto dishDto = new DishDto();
            // 6.2 先把Dish对象的所有属性拷贝到DishDto对象
            BeanUtils.copyProperties(dishItem, dishDto);
            // 6.3 创建DishFlavor的查询条件构造器
            LambdaQueryWrapper<DishFlavor> flavorWrapper = new LambdaQueryWrapper<>();
            // 6.4 添加过滤条件--根据菜品id查询菜品口味
            flavorWrapper.eq(DishFlavor::getDishId, dishItem.getId());
            // 6.5 查询菜品口味
            List<DishFlavor> dishFlavors = dishFlavorService.list(flavorWrapper);
            // 6.6 设置菜品口味
            dishDto.setFlavors(dishFlavors);
            // 6.7 返回封装好的dishDto
            return dishDto;

        }).collect(Collectors.toList());
    }


}
